function [best_param, error_vect, std_vect] = cv_wrapper(X, Y, opt_param, alg, kern)

% This function takes in a column vector OPT_PARAM (optimization parameters) and
% calls the approriate cv function in a loop for each value in OPT_PARAM to
% return the best choice of parameter, along with a matrix of errors and a matrix
% of std devs, both are (length of param)x2. the error columns are [cv_err test_err],
% and the std cols are [cv_std test_std]. It also takes as input a string "ALG"
% that corresponds to the algorithm that is to be used in cross validation. 
% Valid strings for "ALG" are:
%                   'dt'
%                   'knn'
%                   'boost'
%
% This function also takes as input a string "KERN" that corresponds
% to the type of kernel that is to be used. Valid strings
% for "KERN" are:
%                   'poly1'
%                   'poly2'
%                   'poly3'
%                   'gaussian'
%                   'intersection'
% If "KERN" is anything but the above, the kernel will be computed using a
% simple dot product.

n = size(X, 1);
n_folds = 10;

%choose the number of partitions
part = make_cv_partition(n, n_folds);

cv_ind = find(part~= 9 & part~= 10);
test_ind = find(part==9 | part == 10);

%Index the X and Y arrays by the appropriate indicies

cv_points = X(cv_ind,:); %Training set (80%)
cv_labels = Y(cv_ind,:); %Training labels (80 % )
test_points = X(test_ind,:); %Test set (20 %)
test_labels = Y(test_ind,:); % Test labels (20 % )

error_vect = zeros(size(opt_param,1),2);
std_vect = zeros(size(opt_param,1),2);

% converting word counts to probabilities 
% X_kern = bsxfun(@rdivide, X, sum(X, 2));
X_kern = X; 

switch kern
    case 'poly1'
        kernel = kernel_poly(X_kern,X_kern,1);
    case 'poly2'    
        kernel = kernel_poly(X_kern,X_kern,2);
    case 'poly3'
        kernel = kernel_poly(X_kern,X_kern,3);    
    case 'gaussian'
        kernel = kernel_gaussian(X_kern,X_kern,20); %Pick sigma = 20
    case 'intersection'
        kernel = kernel_intersection(X_kern,X_kern);
    otherwise
        kernel = kernel_poly(X_kern,X_kern,1);
end

fprintf('Running CV for %s ...\n',alg);

switch lower(alg)
    case 'dt'
        for i=1:size(opt_param,1)
            [error_vect(i,:) std_vect(i,:)] = cv_dt(opt_param(i), ...
                cv_points, cv_labels, test_points, test_labels);
        end
    case 'knn'
        for i=1:size(opt_param,1)
            [error_vect(i,:) std_vect(i,:)] = cv_knn(opt_param(i), ...
                kernel, Y, cv_ind, test_ind);
        end
    case 'boost'
        for i=1:size(opt_param,1)
            [error_vect(i,:) std_vect(i,:)] = cv_boost(opt_param(i), ...
                kernel, cv_points, cv_labels, test_points, test_labels);
        end
    case 'svm'        
        for i=1:size(opt_param,1)
            fprintf('-- Param: %d \n',opt_param(i));
            
            %liblinear
            %error_vect(i,:) = [liblinear_train(cv_labels, cv_points, ['-v 10 -c ' num2str(opt_param(i))]) 0];
            
            %libsvm (built-in cv)
            error_vect(i,:) = [svmtrain(cv_labels, [(1:size(kernel(cv_ind,cv_ind),1))' kernel(cv_ind,cv_ind)], ...
                               sprintf('-s 0 -t 4 -v 10 -c %g', opt_param(i))) 0];
            std_vect(i,:) = [0 0];
        end
        error_vect(:,1) = 100-error_vect(:,1);  %change from libsvm's cv accuracy (0-1) to cv error (0-1)
        
end

% Find the index of the minimum error, and index the OPT_PARAM vector with
% that index to find the corresponding BEST_PARAM
[~,ind] = min(error_vect(:,1));
best_param = opt_param(ind);

%Plot errors
figure(1); clf; hold on; grid on; 
errorbar(opt_param, error_vect(:,1), std_vect(:,1), '-r', 'LineWidth', 2); 
errorbar(opt_param, error_vect(:,2), std_vect(:,2),'-b', 'LineWidth', 2);
legend('CV Error', 'Test Error'); 

if strcmpi(alg,'dt')
    t = ' , dt';
else
    t = [' , ' alg ' ' kern];
end

title(['CV and True Error' t],'FontSize',20)
xlabel('Parameter Value','FontSize',18)
ylabel('Error','FontSize',18)
hold off; 

end